package com.cis467.mrball;

import com.cis467.mrball.parts.Button;
import com.cis467.mrball.parts.Floor;
import com.cis467.mrball.parts.Level;
import com.cis467.mrball.parts.Portal;
import com.cis467.mrball.parts.Wall;
import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.SurfaceHolder;
import android.view.SurfaceView;

public class BuildLevelView extends SurfaceView implements SurfaceHolder.Callback{
       
    /*
     * Model settings
     * TILE_SIZE is implicitly 1.0
     */
	private static final double WALL_THICKNESS = 0.05;

	// Menu constants
	private static final int NONE = -1;
	private static final int SPECIAL = 0;
	private static final int FLOOR = 1;
	private static final int WALL = 2;
	private static final int BUTTON = 3;
	private static final int PORTAL = 4;
	private static final int REMOVE = 5;
	
	// Removal constants
	private static final int REMOVE_HWALL = 0;
	private static final int REMOVE_VWALL = 1;
	private static final int REMOVE_FLOOR = 2;

    private Context mContext;
    
    private SurfaceHolder mSurfaceHolder;
    
    // For drawing to screen
    private BuildLevelScreenManager mScreenManager;
    
    // The level the is being built
    private Level mLevel;
    
    // The secondary menu that is open
    private int mActiveMenu;
    
    // The secondary menu to which the selected item belongs
    private int mItemMenu;
    
    // The item number of the selected item
    private int mItemNum;
    
    public BuildLevelView(Context context, AttributeSet attrs) {
        
    	super(context, attrs);
        
    	// Save the context
    	mContext = context;
    	
    	// Create a screen manager
    	mScreenManager = new BuildLevelScreenManager(mContext, WALL_THICKNESS);
    	
    	// Setup screen
        mSurfaceHolder = getHolder();
        mSurfaceHolder.addCallback(this);
        mSurfaceHolder.setKeepScreenOn(true);
        setFocusable(true);
        
        if(mLevel == null)
        	// Create a blank level
        	loadLevel(new Level());
    }
    
    public void newLevel() {
    	loadLevel(new Level());
    	draw();
    }
    
    public void loadLevel(Level level) {
    	
    	// Save the level
    	mLevel = level;
    	
    	mActiveMenu = NONE;
    	mItemMenu = NONE;
    	mItemNum = NONE;
    }
    
    public void handleTouch(int x, int y) {
    	
    	// Check if the user touched the side panel
    	if(mScreenManager.isSideMenuTouch(x)) {
    		mActiveMenu = mScreenManager.resolveSideMenuTouch(y);
    		
    		if(mActiveMenu == REMOVE  && mItemMenu != REMOVE) {
    		
    			if(mItemMenu == WALL) {
    				mItemNum = mItemNum % 2;	
    			}
    			else {
    				mItemNum = REMOVE_FLOOR;
    			}
    			
    			mItemMenu = REMOVE;
    		}
    	}
    	
    	// Check if the user touched the bottom panel
    	else if(mActiveMenu != NONE && mScreenManager.isBottomMenuTouch(x,y)) {
    		
    		// Get the selected item number
    		mItemNum = mScreenManager.resolveBottomMenuTouch(x, mActiveMenu);
    		
    		if(mItemNum != NONE) {
    			mItemMenu = mActiveMenu;
    			mActiveMenu = NONE;
    		}
    		else {
    			mItemMenu = NONE;
    			mActiveMenu = NONE;
    		}
    	}
    	
    	// The user touched the level
    	else {
    		
    		// Make sure the submenu is closed
    		mActiveMenu = NONE;
    		
    		// Check if an item is selected
    		if(mItemNum != NONE) {
    		
    			// Resolve touch to location
    			int col = mScreenManager.resolveLevelCol(x, mItemMenu, mItemNum);
    			int row = mScreenManager.resolveLevelRow(y, mItemMenu, mItemNum);
    			
    			// Place item
    			switch(mItemMenu) {
    			
    				case FLOOR: placeFloor(col, row, mItemNum); break;
    				
    				case WALL: placeWall(col, row, mItemNum); break;
    				
    				case BUTTON: placeButton(col, row, mItemNum); break;
    						
    				case PORTAL: placePortal(col, row, mItemNum); break;
    						
    				case SPECIAL: placeSpecial(col, row, mItemNum); break;
    				
    				case REMOVE: removeItem(col, row, mItemNum); break;
    			
    				default: break;
    			}
    			
    		}
    	}
    	
    	draw();  
    }
    
    private void placeFloor(int col, int row, int type) {
    	
    	// Get the current floor type
    	Floor floor = mLevel.floors[col][row];
    	
    	// Handle pits differently so we don't place keys, buttons, ect.. above pits
    	if(type == Floor.PIT) {
    		
    		// Make sure portals, specials are cleaned before overwriting
    		cleanForReplace(floor);
    		
    		// Replace whatever is there with a pit
    		mLevel.floors[col][row] = new Floor(col, row, Floor.PIT);
    	}
    	
    	// Just change the type
    	else {
	    	mLevel.floors[col][row].type = type;
    	}
	}
    
    private void placeWall(int col, int row, int type) {
    	
    	switch(type) {
    		case Wall.HWALL: mLevel.hWalls[col][row] = new Wall(col, row, Wall.NORMAL); break;
    		case Wall.VWALL: mLevel.vWalls[col][row] = new Wall(col, row, Wall.NORMAL); break;
    		case Wall.HLASER: mLevel.hWalls[col][row] = new Wall(col, row, Wall.LASER); break;
    		case Wall.VLASER: mLevel.vWalls[col][row] = new Wall(col, row, Wall.LASER); break;
    	}
	}
    
    private void placeButton(int col, int row, int color) {
    	
    	// Get the current floor type
    	Floor floor = mLevel.floors[col][row];
    	
    	// Ignore pits
    	if(floor.type != Floor.PIT) {
    		
    		// Make sure portals, specials are cleaned before overwriting
    		cleanForReplace(floor);
    		
    		// Make sure we don't end up with more than one button of this color
    		removeExtraButton(color);
    		
    		Button button = new Button(col, row, floor.type, color);
    		
	    	mLevel.floors[col][row] = button;
	    	
	    	mLevel.buttons.add(button);
    	}
    }
    
    private void placePortal(int col, int row, int color) {
    	
    	// Get the current floor
    	Floor floor = mLevel.floors[col][row];
    	
    	// Ignore pits
    	if(floor.type != Floor.PIT) {
    		
    		// Make sure portals, specials are cleaned before overwriting
    		cleanForReplace(floor);
    		 
    		// Make sure we don't end up with more than two portals of this color
    		removeExtraPortal(color);
    		
    		Portal portal = new Portal(col, row, floor.type, color);
    		
	    	mLevel.floors[col][row] = portal;
	    	
	    	mLevel.portals.add(portal);
    	}
    }
    
    private void placeSpecial(int col, int row, int type) {
    	
    	// Get the current floor
    	Floor floor = mLevel.floors[col][row];
    	
    	// Ignore pits
    	if(floor.type != Floor.PIT) {
    		
    		// Make sure portals, specials are cleaned before overwriting
    		cleanForReplace(floor);
    		
    		// Replace the floor, to clear buttons, portals, ect..
    		placeFloor(col, row, floor.type);
    		
    		// Point the appropriate handle here
	    	if(type == Level.START) 
	    		mLevel.start = mLevel.floors[col][row];
	    	else if(type == Level.KEY) 
	    		mLevel.key = mLevel.floors[col][row];
	    	else if(type == Level.END)
	    		mLevel.end = mLevel.floors[col][row];
    	}
    }
    
    private boolean cleanForReplace(Floor floor) {
    	
    	// Check if we are cleaning up the start
		if(mLevel.start != null && floor.equals(mLevel.start)) {
			mLevel.start = null;
			return true;
		}
	
		// Check if we are cleaning up the key
		else if(mLevel.key != null && floor.equals(mLevel.key)) {
			mLevel.key = null;
			return true;
		}
		
		// Check if we are cleaning up the end
		else if(mLevel.end != null && floor.equals(mLevel.end)) {
			mLevel.end = null;
			return true;
		}
    			
    	// Check if we are cleaning up a portal
		else if(floor.isPortal()) {
			mLevel.removePortal((Portal) floor);
			return true;
		}
		
		// Check if we are cleaning up a button
		else if(floor.isButton()) {
			mLevel.removeButton((Button) floor);
			return true;
		}
		
		return false;
    }
    
    private void removeExtraButton(int color) {
    	
    	// Look for any duplicates of the color we are placing
    	for(Button b : mLevel.buttons) {
    		if(b.color == color) {
    			
    			// Replace the button with a floor tile
    			mLevel.floors[b.col][b.row] = new Floor(b.col, b.row, b.type);
    		
    			// Remove the button from the button list
    			mLevel.removeButton(b);
    			
    			// Found it. Need to break the loop 
    			break;
    		}
    	}
    }
    
    private void removeExtraPortal(int color) {
    	
    	Portal noPortal = new Portal(-1,-1);
    	Portal first = noPortal;
    	
    	// Look for any duplicates of the color we are placing
    	for(Portal p : mLevel.portals) {
    		if(p.color == color) {
    			
    			// Check if we've found a first portal of this color yet
    			if(first.equals(noPortal))
    				first = p;
    			
    			// This is the second portal of this color. remove the other one
    			else {
    				
	    			// Replace the first portal with a floor tile
	    			mLevel.floors[first.col][first.row] = new Floor(first.col, first.row, first.type);
	    		
	    			// Remove the portal from the portal list
	    			mLevel.removePortal(first);
	    			
	    			// Found it. Need to break the loop
	    			break;
    			}
    		}
    	}
    }
    
    private void removeItem(int col, int row, int itemNum) {
    	
    	if(itemNum == REMOVE_HWALL) {
    		mLevel.hWalls[col][row] = new Wall(col, row, Wall.NONE);
    	}
    	
    	else if(itemNum == REMOVE_VWALL) {
    		mLevel.vWalls[col][row] = new Wall(col, row, Wall.NONE);
    	}
    	else if(itemNum == REMOVE_FLOOR) {
    		
    		Floor floor = mLevel.floors[col][row];
    		
    		if(!cleanForReplace(floor)) {
    			mLevel.floors[col][row] = new Floor(col, row, Floor.NORMAL);
    		}
    		else {
    			mLevel.floors[col][row] = new Floor(col, row, floor.type);
    		}
    	}
    }
    
    
    private void draw() {
    	
    	Canvas canvas = null;
        
    	try {
        	
    		// Lock and get the canvas
            canvas = mSurfaceHolder.lockCanvas(null);
            
            synchronized (mSurfaceHolder) {
                
            	// Draw the screen
                mScreenManager.draw(canvas, mLevel, mActiveMenu, mItemMenu, mItemNum);
            }
        } 
    	
    	finally {

    		// Check if canvas was retrieved
            if (canvas != null) {
            	
            	// Unlock and draw it
                mSurfaceHolder.unlockCanvasAndPost(canvas);
            }
        }
    }

    /* Callback invoked when the surface dimensions change. */
    public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
    	
    	// Update the screen dimensions
    	mScreenManager.setDimensions(width, height);
    	
    	// Draw the initial screen
    	draw();    
    }

    /*
     * Callback invoked when the Surface has been created and is ready to be
     * used.
     */
    public void surfaceCreated(SurfaceHolder holder) {
    	// Do Nothing
    }

    /*
     * Callback invoked when the Surface has been destroyed and must no longer
     * be touched. WARNING: after this method returns, the Surface/Canvas must
     * never be touched again!
     */
    public void surfaceDestroyed(SurfaceHolder holder) {
    	// Do Nothing
    }
    
    public Level getLevel() {
    	return mLevel;
    }
}

